package mapspinner.persistence.datatypes;

import java.io.IOException;
import java.nio.FloatBuffer;

import com.jme.util.export.InputCapsule;
import com.jme.util.export.JMEImporter;
import com.jme.util.export.OutputCapsule;
import com.jme.util.geom.BufferUtils;

/**
 * CompressedMapData implements mapData in a way that compresses Everything it
 * receives.
 * 
 * @author Administrator
 * 
 */
public class CompressedTerrainData implements TerrainData {
	private static final long serialVersionUID = 3421519256836257000L;
	private short[] heightMap;
	private byte[] colorMap;
	private float heightMapLow = 0, heightMapDataScale = 0;
	private float[] colorMapLow, colorMapDataScale;
	private String[] textures;
	private byte layers;

	public CompressedTerrainData() {
	}

	public CompressedTerrainData(final byte layers) {
		colorMapLow = new float[layers];
		colorMapDataScale = new float[layers];
		this.layers = layers;
	}

	public Class<? extends TerrainData> getClassTag() {
		return this.getClass();
	}

	public FloatBuffer getColorMap() {
		final FloatBuffer retVal = BufferUtils
				.createFloatBuffer(heightMap.length * 4);
		for (int x = 0; x < heightMap.length; x++) {
			if (layers > 0)
				retVal.put(x * 4, colorMapLow[0] + (heightMap[x] & 0x0000000F)
						* colorMapDataScale[0] / 15);

			if (layers > 1) {
				retVal.put(x * 4 + 1, colorMapLow[1]
						+ (colorMap[x] & 0x0000000F) * colorMapDataScale[1]
						/ 15);
				retVal.put(x * 4 + 2, colorMapLow[2]
						+ (Integer.rotateRight(colorMap[x], 4) & 0x0000000F)
						* colorMapDataScale[2] / 15);
			}
		}
		return retVal;
	}

	public float[] getHeightMap() {
		final float[] retVal = new float[heightMap.length];
		for (int x = 0; x < heightMap.length; x++)
			retVal[x] = heightMapLow
					+ (Integer.rotateRight(heightMap[x], 4) & 0x00000FFF)
					* heightMapDataScale / 4096;
		return retVal;
	}

	public String[] getTextures() {
		return textures;
	}

	public void read(final JMEImporter im) throws IOException {
		final InputCapsule capsule = im.getCapsule(this);
		heightMap = capsule.readShortArray("hm", null);
		heightMapLow = capsule.readFloat("hml", 0);
		heightMapDataScale = capsule.readFloat("hmds", 0);
		layers = capsule.readByte("c", (byte) 1);
		colorMap = capsule.readByteArray("cm", null);
		colorMapLow = capsule.readFloatArray("cml", null);
		colorMapDataScale = capsule.readFloatArray("cmds", null);
		textures = capsule.readStringArray("t", new String[] { "darkrock.jpg",
				"nicegrass.jpg" });
	}

	public void setColorMap(final FloatBuffer colorMap) {
		final int length = colorMap.limit() / 4;
		if (heightMap == null || heightMap.length != length)
			heightMap = new short[length];

		if (layers > 1)
			this.colorMap = new byte[colorMap.limit() / 4];
		final float[] high = new float[layers];

		for (int layer = 0; layer < layers; layer++) {
			colorMapLow[layer] = 1.0f;
			high[layer] = 0.0f;
		}

		// determine high, low and size
		for (int layer = 0; layer < layers; layer++) {
			for (int x = 0; x < length; x++) {
				final float element = colorMap.get(x * 4 + layer);
				if (element > high[layer])
					high[layer] = element;
				if (element < colorMapLow[layer])
					colorMapLow[layer] = element;
			}
			colorMapDataScale[layer] = high[layer] - colorMapLow[layer];
		}
		// compress into 4-bit values
		for (int x = 0; x < length; x++) {
			byte setVal;
			if (layers > 0) {
				setVal = (byte) ((colorMap.get(x * 4) - colorMapLow[0])
						/ colorMapDataScale[0] * 15);
				heightMap[x] |= setVal;
			}
			if (layers > 1) {
				setVal = (byte) ((colorMap.get(x * 4 + 1) - colorMapLow[1])
						/ colorMapDataScale[1] * 15);
				this.colorMap[x] |= setVal;
				setVal = (byte) ((colorMap.get(x * 4 + 2) - colorMapLow[2])
						/ colorMapDataScale[2] * 15);
				this.colorMap[x] |= setVal << 4 & 0x000000F0;
			}
		}
	}

	public void setHeightMap(final float[] heightMap) {
		float high = Float.MIN_VALUE;
		heightMapLow = Float.MAX_VALUE;

		if (this.heightMap == null || this.heightMap.length != heightMap.length)
			this.heightMap = new short[heightMap.length];

		// determine high, low and size
		for (final float element : heightMap) {
			if (element > high)
				high = element;
			if (element < heightMapLow)
				heightMapLow = element;
		}
		heightMapDataScale = high - heightMapLow;
		// compress into 12-bit values
		for (int x = 0; x < heightMap.length; x++) {
			final short setVal = (short) ((heightMap[x] - heightMapLow)
					/ heightMapDataScale * 4095);
			this.heightMap[x] |= setVal << 4 & 0x0000FFF0;
		}
	}

	public void setTextures(final String[] textures) {
		this.textures = textures;
	}

	public void write(final com.jme.util.export.JMEExporter ex)
			throws IOException {
		final OutputCapsule capsule = ex.getCapsule(this);
		capsule.write(heightMap, "hm", null);
		capsule.write(heightMapLow, "hml", 0);
		capsule.write(heightMapDataScale, "hmds", 0);
		capsule.write(layers, "c", (byte) 1);
		capsule.write(colorMap, "cm", null);
		capsule.write(colorMapLow, "cml", null);
		capsule.write(colorMapDataScale, "cmds", null);
		capsule.write(textures, "t", new String[] { "darkrock.jpg",
				"nicegrass.jpg" });
	}
}
